// 这是一个使用条件变量来解决“生产者-消费者”问题的实例
#include <stdio.h>
#include <pthread.h>

#define BUFFER_SIZE 4
#define OVER (-1)

// 定义生产者条件变量结构
struct producers{
	
	int buffer[BUFFER_SIZE];	// 定义缓冲区
	pthread_mutex_t lock;		// 定义访问缓冲区的互斥锁
	int readpos,writepos;		// 定义访问缓冲区的互斥锁
	pthread_cond_t notempty;	// 缓冲区由数据时的标记
	pthread_cond_t notfull;		// 缓冲区未满的标记处
};

// 初始化缓冲区
void init(struct producers *b){

	pthread_mutex_init(&b->lock, NULL);
	pthread_cond_init(&b->notempty, NULL);
	pthread_cond_init(&b->notfull, NULL);
	b->readpos = 0;
	b->writepos = 0;
}

// 在缓冲区中存放一个整数
void put(struct producers *b, int data){
	
	pthread_mutex_lock(&(b->lock));

	// 当缓冲区为满时等待
	while((b->writepos+1)%BUFFER_SIZE == b->readpos){
		// 在返回之前，pthread_cond_wait需要参数b->lock
		pthread_cond_wait(&b->notfull, &b->lock);
	}

	// 向缓冲区中写数据，并将指针向前移动
	b->buffer[b->writepos] = data;
	b->writepos++;
	
	if(b->writepos >= BUFFER_SIZE){
		b->writepos = 0;
	}

	// 发送当前缓冲区有数据的信号
	pthread_cond_signal(&b->notempty);
	pthread_mutex_unlock(&b->lock);

}

// 从缓冲区中读取数据并将数据从缓冲区中移走
int get(struct producers *b){
	
	int data;
	pthread_mutex_lock(&b->lock);
	// 当缓冲区中有数据时等待
	while(b->writepos == b->readpos){
		pthread_cond_wait(&b->notempty, &b->lock);
	}

	// 从缓冲区中读数据，并将指针前移
	data = b->buffer[b->readpos];
	b->readpos++;
	if(b->readpos >= BUFFER_SIZE){
		b->readpos = 0;
	}

	// 发送当前缓冲区未满的信号
	pthread_cond_signal(&b->notfull);
	pthread_mutex_unlock(&b->lock);

	return data;
}

struct producers buffer;

// 这是生产者的线程处理函数
void *producer(void *data){
	int n;

	// 连续生产10次
	for(n=0;n<10;n++){
		printf("生产者:%d-->\n",n);
		put(&buffer,n);
	}

	// 将状态放入buffer中
	put(&buffer, OVER);

	return NULL;

}

// 这是消费者的线程生产处理函数
void *consumer(void *data){
	int d;

	while(1){
		d = get(&buffer);	// 从buffer从读取对应的状态
		// 从buffer中读取对应的状态，若果没有则停止
		if(d==OVER){
			break;
		}
		printf("消费者:-->%d\n",d);
	}
	return NULL;
}

// 主程序
int main(void){
	pthread_t thproducer,thconsumer;	//生产者和消费者的id
	void *retval;
	init(&buffer);		// 初始化缓冲区

	// 创建两个线程
	pthread_create(&thproducer, NULL, producer, 0);
	pthread_create(&thconsumer, NULL, consumer, 0);

	// 阻塞线程
	pthread_join(thproducer, &retval);
	pthread_join(thconsumer, &retval);
}

